home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / umich / network / ka9q / ka9q_src.arc / SMTPSERV.C < prev    next >
C/C++ Source or Header  |  1988-07-28  |  13KB  |  595 lines

  1. /* SMTP Server state machine - see RFC 821
  2.  * Very simple implementation; no forwarding allowed
  3.  * (who wants to re-create "sendmail" ??)
  4.  *  enhanced 12/87 Dave Trulli nn2z
  5.  */
  6. #include <stdio.h>
  7. #include <ctype.h>
  8. #include <time.h>
  9. #include "global.h"
  10. #include "mbuf.h"
  11. #include "netuser.h"
  12. #include "timer.h"
  13. #include "tcp.h"
  14. #include "smtp.h"
  15.  
  16. #ifndef DFLT_MODE
  17. #define DFLT_MODE 0660            /* use this instead of user's umask */
  18. #endif
  19.  
  20. char *ptime(), *getname();
  21. void mail_delete(), del_rcpt();
  22. static int queuejob(),checkaddress();
  23. int32 get_msgid();
  24.  
  25. /* Command table */
  26. static char *commands[] = {
  27.     "helo",
  28. #define HELO_CMD    0
  29.     "noop",
  30. #define NOOP_CMD    1
  31.     "mail from:",
  32. #define MAIL_CMD    2
  33.     "quit",
  34. #define QUIT_CMD    3
  35.     "rcpt to:",
  36. #define RCPT_CMD    4
  37.     "help",
  38. #define HELP_CMD    5
  39.     "data",
  40. #define DATA_CMD    6
  41.     "rset",
  42. #define RSET_CMD    7
  43.     NULLCHAR
  44. };
  45.  
  46. /* Reply messages */
  47. static char help[] = "214-Commands:\r\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET\r\n214 End\r\n";
  48. static char banner[] = "220 %s SMTP ready\r\n";
  49. static char closing[] = "221 Closing\r\n";
  50. static char ok[] = "250 Ok\r\n";
  51. static char reset[] = "250 Reset state\r\n";
  52. static char sent[] = "250 Sent\r\n";
  53. static char ourname[] = "250 %s, \"Gateway to the universe!\"\r\n"; /*Share and Enjoy!\r\n";*/
  54. static char enter[] = "354 Enter mail, end with .\r\n";
  55. static char ioerr[] = "452 Temp file write error\r\n";
  56. static char mboxerr[] = "452 Mailbox %s write error\r\n";
  57. static char badcmd[] = "500 Command unrecognized\r\n";
  58. static char syntax[] = "501 Syntax error\r\n";
  59. static char needrcpt[] = "503 Need RCPT (recipient)\r\n";
  60. static char badname[] = "550 Can't open mailbox for %s\r\n";
  61.  
  62. static struct tcb *smtp_tcb;
  63. /* Start up SMTP receiver service */
  64. smtp_start(argc,argv)
  65. int argc;
  66. char *argv[];
  67. {
  68.     struct socket lsocket;
  69.     void r_mail(),s_mail();
  70.  
  71.     lsocket.address = ip_addr;
  72.     if(argc < 2)
  73.         lsocket.port = SMTP_PORT;
  74.     else
  75.         lsocket.port = atoi(argv[1]);
  76.  
  77.     smtp_tcb = open_tcp(&lsocket,NULLSOCK,
  78.         TCP_SERVER,0,r_mail,NULLVFP,s_mail,0,(char *)NULL);
  79. }
  80.  
  81. /* Shutdown SMTP service (existing connections are allowed to finish) */
  82. smtp_stop()
  83. {
  84.     if(smtp_tcb != NULLTCB)
  85.         close_tcp(smtp_tcb);
  86. }
  87.  
  88. /* SMTP connection state change upcall handler */
  89. static void
  90. s_mail(tcb,old,new)
  91. struct tcb *tcb;
  92. char old,new;
  93. {
  94.     struct mail *mp,*mail_create();
  95.  
  96.     switch(new){
  97. #ifdef    QUICKSTART
  98.     case SYN_RECEIVED:
  99. #else
  100.     case ESTABLISHED:
  101. #endif
  102.         if((mp = mail_create(tcb)) == NULLMAIL){
  103.             close_tcp(tcb);
  104.             break;
  105.         }
  106.         (void) tprintf(mp->tcb,banner,hostname);
  107.         log(tcb,"open SMTP");
  108.         break;        
  109.     case CLOSE_WAIT:
  110.         close_tcp(tcb);
  111.         break;
  112.     case CLOSED:
  113.         log(tcb,"close SMTP");
  114.         mp = (struct mail *)tcb->user;
  115.         mail_delete(mp);                
  116.         del_tcp(tcb);
  117.         /* Check if server is being shut down */
  118.         if(tcb == smtp_tcb)
  119.             smtp_tcb = NULLTCB;
  120.         break;
  121.     }
  122. }
  123.  
  124. /* SMTP receiver upcall handler */
  125. static void
  126. r_mail(tcb,cnt)
  127. struct tcb *tcb;
  128. int16 cnt;
  129. {
  130.     register struct mail *mp;
  131.     char *inet_ntoa(),c;
  132.     struct mbuf *bp;
  133.     void docommand(),deliver(),doline();
  134.  
  135.     if((mp = (struct mail *)tcb->user) == NULLMAIL){
  136.         /* Unknown session */
  137.         close_tcp(tcb);
  138.         return;
  139.     }
  140.     recv_tcp(tcb,&bp,cnt);
  141.     /* Assemble an input line in the session buffer.
  142.      * Return if incomplete
  143.      */
  144.     while(pullup(&bp,&c,1) == 1){
  145.         switch(c){
  146.         case '\r':    /* Strip cr's */
  147. #ifdef MSDOS
  148.         case '\032':    /* Strip ctrl/Z's */
  149. #endif
  150.             continue;
  151.         case '\n':    /* Complete line; process it */
  152.             mp->buf[mp->cnt] = '\0';
  153.             doline(mp);
  154.             break;
  155.         default:    /* Assemble line */
  156.             if(mp->cnt != LINELEN-1)
  157.                 mp->buf[mp->cnt++] = c;
  158.             break;
  159.         }
  160.     }
  161. }
  162. /* Process a line read on an SMTP connection (any state) */
  163. static void
  164. doline(mp)
  165. register struct mail *mp;
  166. {
  167.     void docommand(),deliver();
  168.  
  169.     switch(mp->state){
  170.     case COMMAND_STATE:
  171.         docommand(mp);
  172.         break;
  173.     case DATA_STATE:
  174.         tcp_output(mp->tcb);    /* Send ACK; disk I/O is slow */
  175.         if(mp->buf[0] == '.' && mp->buf[1] == '\0'){
  176.             mp->state = COMMAND_STATE;
  177.             deliver(mp);    /* Also sends appropriate response */
  178.             break;
  179.         }
  180.         /* Append to data file */
  181.         if(fprintf(mp->data,"%s\n",mp->buf) < 0){
  182.             mp->state = COMMAND_STATE;
  183.             (void) tprintf(mp->tcb,ioerr);
  184.         }
  185.         break;
  186.     }
  187.     mp->cnt = 0;
  188. }
  189. /* Create control block, initialize */
  190. static struct mail *
  191. mail_create(tcb)
  192. register struct tcb *tcb;
  193. {
  194.     register struct mail *mp;
  195.  
  196.     if((mp = (struct mail *)calloc(1,sizeof (struct mail))) == NULLMAIL)
  197.         return NULLMAIL;
  198.     mp->tcb = tcb;        /* Downward pointer */
  199.     tcb->user = (char *)mp; /* Upward pointer */
  200.     return mp;
  201. }
  202.  
  203. /* Free resources, delete control block */
  204. static void
  205. mail_delete(mp)
  206. register struct mail *mp;
  207. {
  208.  
  209.     if (mp == NULLMAIL)
  210.         return;
  211.     if(mp->system != NULLCHAR)
  212.         free(mp->system);
  213.     if(mp->from != NULLCHAR)
  214.         free(mp->from);
  215.     if(mp->data != NULLFILE)
  216.         fclose(mp->data);
  217.     del_rcpt(mp->to);
  218.     free((char *)mp);
  219. }
  220.  
  221. static void
  222. del_rcpt(p)
  223. struct addr *p;
  224. {
  225.     register struct addr *ap,*ap1;
  226.     for(ap = p;ap != NULLADDR;ap = ap1){
  227.         if(ap->val != NULLCHAR)
  228.             free(ap->val);
  229.         ap1 = ap->next;
  230.         free((char *)ap);
  231.     }
  232. }
  233.  
  234. /* Parse and execute mail commands */
  235. static void
  236. docommand(mp)
  237. register struct mail *mp;
  238. {
  239.     char *cmd,*arg,*cp,**cmdp;
  240.     struct addr *ap;
  241.     FILE *tmpfile();
  242.     long t;
  243.  
  244.     cmd = mp->buf;
  245.     if(mp->cnt < 4){
  246.         /* Can't be a legal SMTP command */
  247.         (void) tprintf(mp->tcb,badcmd);
  248.         return;
  249.     }    
  250.     cmd = mp->buf;
  251.  
  252.     /* Translate entire buffer to lower case */
  253.     for(cp = cmd;*cp != '\0';cp++)
  254.         *cp = tolower(*cp);
  255.  
  256.     /* Find command in table; if not present, return syntax error */
  257.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  258.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  259.             break;
  260.     if(*cmdp == NULLCHAR){
  261.         (void) tprintf(mp->tcb,badcmd);
  262.         return;
  263.     }
  264.     arg = &cmd[strlen(*cmdp)];
  265.     /* Skip spaces after command */
  266.     while(*arg == ' ')
  267.         arg++;
  268.     /* Execute specific command */
  269.     switch(cmdp-commands){
  270.     case HELO_CMD:
  271.         if(mp->system != NULLCHAR)
  272.             free(mp->system);
  273.         if((mp->system = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  274.             /* If the system is out of memory, just close */
  275.             close_tcp(mp->tcb);
  276.             break;            
  277.         } else {
  278.             strcpy(mp->system,arg);
  279.             (void) tprintf(mp->tcb,ourname,hostname);
  280.         }
  281.         break;
  282.     case NOOP_CMD:
  283.         (void) tprintf(mp->tcb,ok);
  284.         break;
  285.     case MAIL_CMD:
  286.         if(mp->from != NULLCHAR)
  287.             free(mp->from);
  288.         if((mp->from = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  289.             /* If the system is out of memory, just close */
  290.             close_tcp(mp->tcb);
  291.             break;            
  292.         } else {
  293.             if((cp = getname(arg)) == NULLCHAR){
  294.                 (void) tprintf(mp->tcb,syntax);
  295.                 break;
  296.             }
  297.             strcpy(mp->from,cp);
  298.             (void) tprintf(mp->tcb,ok);
  299.         }
  300.         break;
  301.     case QUIT_CMD:
  302.         (void) tprintf(mp->tcb,closing);
  303.         close_tcp(mp->tcb);
  304.         break;
  305.     case RCPT_CMD:    /* Specify recipient */
  306.         if((cp = getname(arg)) == NULLCHAR){
  307.             (void) tprintf(mp->tcb,syntax);
  308.             break;
  309.         }
  310.         if (checkaddress(cp)) {
  311.             (void) tprintf(mp->tcb,badname,cp);
  312.             break;
  313.         }
  314.         /* Allocate an entry on the recipient list. This
  315.          * assembles the list backwards, but what the heck.
  316.          */
  317.         if((ap = (struct addr *)malloc(sizeof(struct addr))) == NULLADDR){
  318.             close_tcp(mp->tcb);
  319.             break;
  320.         }
  321.         if((ap->val = malloc((unsigned)strlen(cp)+1)) == NULLCHAR){
  322.             free((char *)ap);
  323.             close_tcp(mp->tcb);
  324.             break;
  325.         }
  326.         strcpy(ap->val,cp);
  327.         ap->next = mp->to;
  328.         mp->to = ap;
  329.         (void) tprintf(mp->tcb,ok);
  330.         break;
  331.     case HELP_CMD:
  332.         (void) tprintf(mp->tcb,help);
  333.         break;
  334.     case DATA_CMD:
  335.         if(mp->to == NULLADDR){
  336.             (void) tprintf(mp->tcb,needrcpt);
  337.             break;
  338.         }
  339.         tcp_output(mp->tcb);    /* Send ACK; disk I/O is slow */
  340.         if((mp->data = tmpfile()) == NULLFILE){
  341.             (void) tprintf(mp->tcb,ioerr);
  342.             break;
  343.         }
  344.         /* Add timestamp; ptime adds newline */
  345.         mp->seqn = get_msgid();
  346.         time(&t);
  347.         fprintf(mp->data,"Received: ");
  348.         if(mp->system != NULLCHAR)
  349.             fprintf(mp->data,"from %s ",mp->system);
  350.         fprintf(mp->data,"by %s with SMTP (871225.4/ST)\n\tid %ld; %s",
  351.                 hostname,
  352.                 mp->seqn,
  353.                 ptime(&t));
  354.         if(ferror(mp->data)){
  355.             (void) tprintf(mp->tcb,ioerr);
  356.         } else {
  357.             mp->state = DATA_STATE;
  358.             (void) tprintf(mp->tcb,enter);
  359.         }
  360.         break;
  361.     case RSET_CMD:
  362.         del_rcpt(mp->to);
  363.         mp->to = NULLADDR;
  364.         mp->state = COMMAND_STATE;
  365.         (void) tprintf(mp->tcb,reset);
  366.         break;
  367.     }
  368. }
  369. /* Given a string of the form <user@host>, extract the p